#pragma once
#include <iostream>
#include <memory>
#include "Socket.hpp"
using namespace std;

class SelectSever
{
    const static int N = sizeof(fd_set) * 8;
    const static int defaultfd = -1;

public:
    SelectSever(uint16_t port)
        : _port(port), _listensock(make_unique<TcpSocket>())
    {
        InetAddr inet("0", port);
        _listensock->BuildListenSocket(inet);
        // 初始化辅助数组
        for (int i = 0; i < N; i++)
        {
            rfds_arr[i] = defaultfd;
        }
        rfds_arr[0] = _listensock->sockfd(); //辅助数组中第一个元素一定是监听fd
    }

    // listen读的行为
    void AcceptClient()
    {
        InetAddr client;
        int sockfd = _listensock->AcceptSocket(&client);
        if (sockfd < 0)
            return;
        LOG(INFO, "accept success: ip: %s port : %d\n", client.Ip().c_str(), client.Port());
        // 已经就绪的fd，除了listen的，还有读写fd,把新的fd先加进去辅助数组的空位
        int pos = 1;
        for (; pos < N; pos++)
        {
            if (rfds_arr[pos] == defaultfd)
                break;
        }
        if (pos == N)
        {
            // 连接满了，关掉连接
            ::close(sockfd);
            LOG(INFO, "rfds full  close sockfd\n");
            return;
        }
        else
        {
            rfds_arr[pos] = sockfd;
            // 添加成功
            LOG(INFO, "add rfds_arr success,sockfd: \n", sockfd);
        }
        //看看现在有几个fd
        LOG(INFO, "now rfdlist: %s\n", RfdsList().c_str());
    }

    // 普通读fd行为
    void ServiceIO(int pos)
    {
        char buffer[1024];
        ssize_t n = ::recv(rfds_arr[pos], buffer, sizeof(buffer) - 1, 0);
        if (n > 0)
        {
            buffer[n] = 0;
            cout << "client say# " << buffer << endl;
            // 服务器回显回去
            string echo_string = "[Sever echo]#";
            echo_string += buffer;
            ::send(rfds_arr[pos], echo_string.c_str(), echo_string.size(), 0);
        }
        // 对方不发数据了，或者对方把链接关了，我也直接关掉
        else if (n == 0)
        {
            LOG(DEBUG, "%d is close\n", rfds_arr[pos]);
            close(rfds_arr[pos]);
            // 辅助数组中这里设-1,代表这个已经读完了
            rfds_arr[pos] = defaultfd;
            LOG(DEBUG, "now rfdslist:%s \n", RfdsList().c_str());
        }
        //出错
        else
        {
            LOG(ERROR, "%d recv fail\n", rfds_arr[pos]);
            close(rfds_arr[pos]);
            rfds_arr[pos] = defaultfd;
            LOG(DEBUG, "now rfdslist:%s \n", RfdsList().c_str());
        }
    }
    // 处理辅助数组中就绪的fd
    void HandleTask(fd_set &rfds)
    {
        for (int i = 0; i < N; i++)
        {
            //辅助数组中会存在不合法的，因为读fd有两种情况都会给位置置-1，所以处理时需要判断 必须是就绪的fd
            if (rfds_arr[i] == defaultfd)
                continue;
            if (FD_ISSET(rfds_arr[i], &rfds))
            {
                // 只关心读，而读有listen 和 其他读fd，而两者行为不一样，所以还得加判断
                if (rfds_arr[i] == _listensock->sockfd())
                {
                    AcceptClient();
                }
                else
                {
                    ServiceIO(i);
                }
            }
        }
    }
    void Loop()
    {
        while (1)
        {
            // 需要一个位图结构
            fd_set rfds;
            FD_ZERO(&rfds);
            // 每次清空后，重新把辅助数组中的加进去
            int max_rfd = -1;
            for (int i = 0; i < N; i++)
            {
                if (rfds_arr[i] == defaultfd)
                    continue;
                FD_SET(rfds_arr[i], &rfds);
                // 更新最大fd
                if (max_rfd < rfds_arr[i])
                    max_rfd = rfds_arr[i];
            }
            struct timeval tvl = {2, 0};
            int n = select(max_rfd + 1, &rfds, nullptr, nullptr, &tvl);
            switch (n)
            {
            case 0:
                LOG(INFO, "select overtime,%d.%d\n", tvl.tv_sec, tvl.tv_usec);
                break;
            case -1:
                LOG(ERROR, "select error\n");
                break;
            default:
                LOG(INFO, "select success n:%d,next: handle task\n", n);
                HandleTask(rfds);
                break;
            }
        }
    }

    string RfdsList()
    {
        string rfdlist;
        for (int i = 0; i < N; i++)
        {
            if (rfds_arr[i] == defaultfd)
                continue;
            rfdlist += to_string(rfds_arr[i]);
            rfdlist += " ";
        }
        return rfdlist;
    }
    ~SelectSever()
    {
    }

private:
    uint16_t _port;
    unique_ptr<Socket> _listensock;
    int rfds_arr[N]; // 辅助数组(每次select前都要重新把 新加的 和 上次的 fd 一起加进去，防止timeval时间到，漏掉处理)
};
